use crate::{constants::{DefaultChunkPointer}, recipe::FpPointer};
use sled::IVec;
use util::RawData;

use super::error::{IndexResult, IndexError};

pub type SfChunkInfo = FpPointer;
pub const CHUNK_INFO_SIZE:usize = std::mem::size_of::<SfChunkInfo>();
pub const IVEC_LAYOUT_SIZE:usize = std::mem::size_of::<SfValueIVecLayout>();

#[derive(Debug, RawData, Clone, Copy)]
pub struct SfValueIVecLayout {
    info:SfChunkInfo,
}

impl SfValueIVecLayout {
    pub fn new(info:SfChunkInfo) -> Self {
        Self {
            info:info,
        }
    }
    
    pub fn from_ivec(v:&IVec) -> &SfValueIVecLayout {
        unsafe { 
            assert_eq!(v.len(),IVEC_LAYOUT_SIZE);
            &*((v as &[u8]) as *const [u8] as *const u8 as usize as *const SfValueIVecLayout)
        }
    }

    pub fn from_ivec_mut(v:&mut IVec) -> &mut SfValueIVecLayout {
        unsafe { 
            assert_eq!(v.len(),IVEC_LAYOUT_SIZE);
            &mut *((v as &mut [u8]) as *mut [u8] as *mut u8 as usize as *mut SfValueIVecLayout)
        }
    }

    pub fn unwrap(self) -> SfChunkInfo{
        self.info
    } 
}

pub trait SfKvStore {
    fn insert_with_feature(&self,feature:u64,info:&DefaultChunkPointer) -> IndexResult<()>;
    fn find_with_feature(&self,feature:u64) -> IndexResult<Option<Box<DefaultChunkPointer>>>;
}

impl SfKvStore for sled::Tree {
    fn insert_with_feature(&self,feature:u64,info:&DefaultChunkPointer) -> IndexResult<()> { 
        self.fetch_and_update(feature.as_raw_data(), |old|{
            match old {
                Some(old) => {
                    let mut ivec = IVec::from(old);
                    let ivec_layout = SfValueIVecLayout::from_ivec_mut(&mut ivec);
                    ivec_layout.info = *info;
                    Some(ivec)
                }
                None => {
                    let ivec_layout = SfValueIVecLayout::new(*info);
                    let ivec = IVec::from(ivec_layout.as_raw_data());
                    Some(ivec)
                }
            }
        })
        .map(|_|())
        .map_err(|e|IndexError::SfInsert(e))
    }

    fn find_with_feature(&self,feature:u64) -> IndexResult<Option<Box<DefaultChunkPointer>>> {
        self.get(feature.as_raw_data())
            .map(|value|value.map(|ivec|{
               Box::new(SfValueIVecLayout::from_ivec(&ivec).clone().unwrap())
            }))
            .map_err(|e|IndexError::SfFind(e))
    }
}